using System;
using System.Collections.Concurrent;
using System.Threading;
using Kiwi.Node.Modules.Builtin;

namespace Kiwi.Node
{
    public class EventQueue : IEventQueue
    {
        readonly ManualResetEvent _signal = new ManualResetEvent(false);
        readonly ConcurrentQueue<Action> _events = new ConcurrentQueue<Action>();
        readonly ConcurrentBag<EventEmitter> _eventEmitters = new ConcurrentBag<EventEmitter>();

        public event Action Idle;
        public event Action Shutdown;

        public void Enque(Action @event)
        {
            _events.Enqueue(@event);
            _signal.Set();
        }

        public bool RunEventLoop(Func<bool> queryContinue)
        {
            while (!_eventEmitters.IsEmpty)
            {
                if (!queryContinue())
                {
                    break;
                }
                _signal.WaitOne(TimeSpan.FromSeconds(1));
                Action action;
                var isIdle = true;
                while (_events.TryDequeue(out action))
                {
                    action();
                    isIdle = false;
                }
                _signal.Reset();
                if (!_events.IsEmpty)
                {
                    _signal.Set();
                }
                if (isIdle && (Idle != null))
                {
                    Idle();
                }
            }
            if (Shutdown != null)
            {
                Shutdown();
                return false;
            }
            return !_eventEmitters.IsEmpty;
        }

        public void RegisterEventEmitter(EventEmitter eventEmitter)
        {
            _eventEmitters.Add(eventEmitter);
        }

        protected void RunEventLoop(CancellationToken cancellationToken)
        {
            while (!_eventEmitters.IsEmpty)
            {
                cancellationToken.ThrowIfCancellationRequested();

                _signal.WaitOne(TimeSpan.FromSeconds(1));
                Action action;
                var isIdle = true;
                while (_events.TryDequeue(out action))
                {
                    action();
                    isIdle = false;
                }
                _signal.Reset();
                if (!_events.IsEmpty)
                {
                    _signal.Set();
                }
                if (isIdle && (Idle != null))
                {
                    Idle();
                }
            }
        }
    }
}